Skip to content

handlers: audit + sharpen every agent_action string per uniform contract (U3)#42

Merged
mastermanas805 merged 1 commit into
masterfrom
pricing/u3-sharpen-agent-actions-fresh
May 12, 2026
Merged

handlers: audit + sharpen every agent_action string per uniform contract (U3)#42
mastermanas805 merged 1 commit into
masterfrom
pricing/u3-sharpen-agent-actions-fresh

Conversation

@mastermanas805
Copy link
Copy Markdown
Member

Summary

Establishes a single source of truth for every agent_action string the API returns (internal/handlers/agent_action.go) and enforces a four-requirement contract via TestAgentActionContract.

Today's strings worked but were inconsistent — some imperative, some passive; some named the tier, some said "their plan"; some included the full URL, some only /pricing. This PR audits all 25 strings across handlers/ + middleware/, sharpens every one to a uniform contract, and extracts each to a named constant or builder so reviewers audit prose in one place.

The U3 contract — every agent_action MUST:

  1. Open with "Tell the user" — the LLM agent re-articulates verbatim to the human.
  2. Name the specific reason rejected (tier, limit, policy, resource) — no "their plan does not allow X".
  3. Name the exact next action (Upgrade, Claim, Provision a twin, Contact support) — never "try again later" without context.
  4. Include a full https://instanode.dev/ URL — no relative /pricing.
  5. (Soft) Under 280 chars so LLMs reproduce verbatim instead of paraphrasing.

The full contract block is committed at the top of internal/handlers/agent_action.go.

Audit counts

Count
Strings audited (before) 25
Strings meeting all 4 contract requirements (before) ~11
Strings meeting all 4 contract requirements (after) 25

Example before/after

deployment_limit_reached (deploy.go):

  • Before: "Tell the user they've hit the %s tier deployment cap (%d apps). Upgrade them to Pro for 10 medium deploys: https://instanode.dev/start?t=..." (broken placeholder URL)
  • After: "Tell the user they've hit the %s tier deployment cap (%d apps). Upgrade to Pro for 10 deployments at https://instanode.dev/pricing — takes 30 seconds, no card for upgrade preview."

upgrade_required (helpers.go registry):

  • Before: "Tell the user this feature requires a higher plan. Have them upgrade at https://instanode.dev/pricing." (vague — "higher plan" gives the LLM nothing concrete)
  • After: "Tell the user this feature requires the Pro plan or higher. Upgrade at https://instanode.dev/pricing — takes 30 seconds."

missing_image_ref (stack.go promote):

  • Before: "This stack predates the image-ref persistence migration. Redeploy the source stack first so its image is cached, then promote." (no "Tell the user" prefix, no URL)
  • After: "Tell the user this stack predates the image-ref persistence migration, so promote has nothing to redeploy. Redeploy the source stack first at https://instanode.dev/app/stacks, then retry the promote."

Pushback / scope notes

  • The middleware/env_policy.go string lives in a different package so it's kept inline (with a same-shape rewrite + an explicit "must stay in sync with agent_action.go" comment). No package-graph reshuffling to avoid blast radius.
  • internal/plans/TestAll_ReturnsAllPlans fails on master and remains failing — pre-existing, unrelated. Filed as known-issue rather than fixed here.
  • 503 / transient infra responses (provision_failed, list_failed, stream_failed, etc.) deliberately omit agent_action per codeToAgentAction curation comments — there's no action the user can take. The one exception (resource_binding_lookup_failed) keeps an agent_action because the actionable advice is concrete: "retry in ~10 seconds, check status page".

Coordination with P1, U1, U2

P1, U1, U2 add new agent_action strings — those tracks should land their strings as named constants/builders in internal/handlers/agent_action.go so the audit + the contract-test stay one-stop. The contract block at the top of that file is the canonical reference.

Test plan

  • go build ./... + go vet ./... clean
  • TestAgentActionContract (new) — every static const + every builder + every registry entry passes all 4 contract checks
  • TestAgentActionContract_RegistryCoverage — all expected error codes registered
  • TestRespondError_* — existing tests updated to match sharpened registry strings, all green
  • internal/handlers/ full suite green (25.9s)
  • internal/middleware/ full suite green (after one flaky-Redis retry — same flake exists on master)
  • All non-e2e packages green except pre-existing internal/plans failure (unrelated)
  • No deploy required for U3 — pure prose change with test gates

🤖 Generated with Claude Code

…act (U3)

Establishes a single source of truth for every agent_action string the API
returns (internal/handlers/agent_action.go) and enforces a four-requirement
contract in tests (TestAgentActionContract).

The U3 contract every agent_action MUST satisfy:
  1. Open with "Tell the user" — the LLM agent re-articulates verbatim.
  2. Name the specific reason rejected (tier, limit, policy, resource).
  3. Name the exact next action (Upgrade, Claim, Provision twin, Contact
     support) — never "try again later" without context.
  4. Include a full https://instanode.dev/ URL — no relative paths.
  5. Under 280 chars so LLMs reproduce verbatim instead of summarizing.

Audited 25 strings across the handlers + middleware packages. Every one now
meets all four requirements: builders (newAgentAction*) for tier/env/limit
interpolation, static constants for fixed walls, and the existing
codeToAgentAction registry sharpened in lockstep.

Call sites refactored to consume the named constants/builders so reviewers
audit prose in one file (agent_action.go) rather than scattered handlers.

Tests:
- TestAgentActionContract covers every string (static + builder + registry)
- TestAgentActionContract_RegistryCoverage guards expected codes present
- Existing TestRespondError_* assertions updated to match sharpened copy
- All handlers + middleware unit tests green (pre-existing internal/plans
  failure unrelated to this PR)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@mastermanas805 mastermanas805 merged commit 2273866 into master May 12, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant